package org.ncibi.cytoscape.metscape.network;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.ncibi.cytoscape.metscape.data.Attributes;
import org.ncibi.cytoscape.metscape.data.Concept;
import org.ncibi.cytoscape.metscape.data.ConceptData;
import org.ncibi.cytoscape.metscape.data.Organism;
import org.ncibi.cytoscape.metscape.plugin.MetScapePlugin;
import org.ncibi.metab.network.MetabolicNetwork;
import org.ncibi.metab.network.edge.MetabolicEdge;
import org.ncibi.metab.network.node.MetabolicNode;

import cytoscape.CyEdge;
import cytoscape.CyNetwork;
import cytoscape.CyNode;
import cytoscape.Cytoscape;
import cytoscape.data.CyAttributesUtils;
import cytoscape.groups.CyGroup;
import cytoscape.groups.CyGroupManager;

public class ExpandInExistingNetworkTranslator extends DefaultNetworkTranslator {

	private CyNetwork network;
	private String cid;
	
	public ExpandInExistingNetworkTranslator(String cid, MetabolicNetwork sourceNetwork, CyNetwork network){
		super(network.getTitle(), sourceNetwork, null, null);
		this.network = network;
		this.cid = cid;
		this.compoundData = MetScapePlugin.getPluginData().getCompoundDataStore()
			.get(Attributes.network.getIntegerAttribute(network.getIdentifier(), "CompoundData"));
		this.geneData = MetScapePlugin.getPluginData().getGeneDataStore()
			.get(Attributes.network.getIntegerAttribute(network.getIdentifier(), "GeneData"));
		this.organism = Organism.toOrganism(Attributes.network.getIntegerAttribute(network.getIdentifier(), "Taxid"));
		this.compoundNodeTranslator = new CompoundNodeTranslator(compoundData);
		this.geneNodeTranslator = new GeneNodeTranslator(geneData,organism);
	}
	
	@Override
	public CyNetwork toCytoscapeNetwork() {
		
		List<String> expansionNodeIds = new ArrayList<String>();
		List<String> expansionEdgeIds = new ArrayList<String>();
		List<?> expandedFrom = new ArrayList<String>(); 
		boolean networkExpanded = false;
		if(Attributes.node.getListAttribute(cid, "inExpansionFor." + networkName) != null) {
			expandedFrom = Attributes.node.getListAttribute(cid, "inExpansionFor." + networkName);
		}
		ConceptData conceptData = MetScapePlugin.getPluginData().getConceptDataStore()
			.get(Attributes.network.getIntegerAttribute(network.getIdentifier(), "ConceptData"));
		for (MetabolicNode node : sourceNetwork.getAllNodes())
        {
			CyNode cyNode = getTranslatorForNode(node).toCyNode(node);
			String id = cyNode.getIdentifier();
			if(!network.containsNode(cyNode)) {
				networkExpanded = true;
				Attributes.node.setAttribute(cyNode.getIdentifier(), "Category." + networkName, 
						Attributes.node.getStringAttribute(cyNode.getIdentifier(), "Type"));
				Attributes.node.setAttribute(id, "isExpansion." + networkName, true);
				Attributes.node.setAttribute(id, "isExpansionSeed." + networkName, false);
				Attributes.node.setAttribute(id, "isSubnetworkExpansionSeed." + networkName, false);
				network.addNode(cyNode);
				List<Concept> concepts = conceptData.getConceptMap().get(cyNode.getIdentifier());
	        	if(concepts != null)
	            for(Concept concept: concepts) {
	            	CyGroup group = CyGroupManager.findGroup(network.getIdentifier() + "-" + concept.getConceptName());
	            	if(group == null) {
	            		group = CyGroupManager.createGroup
	            			(network.getIdentifier() + "-" + concept.getConceptName(), null, network);
	            		CyNode groupNode = group.getGroupNode();
	            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.name", concept.getConceptName());
	            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.numUniqueGenes", concept.getNumUniqueGenes());
	            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.numGenesInNetwork", 0);
	            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.pvalue", concept.getPvalue());
	            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.fdr", concept.getFdr());
	            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.direction", concept.getDirection());
	            	}
	            	if(Attributes.node.getStringAttribute(cyNode.getIdentifier(), "Type").equals("Gene")) {
	            		CyNode groupNode = group.getGroupNode();
	            		Integer numGenesInNetwork = Attributes.node.getIntegerAttribute(groupNode.getIdentifier(), "Concept.numGenesInNetwork");
	            		Attributes.node.setAttribute(groupNode.getIdentifier(), "Concept.numGenesInNetwork", ++numGenesInNetwork);
	            	}
	            	group.addNode(cyNode);
	            }
			}
			
			if( (id != cid) && !expandedFrom.contains(id)
					&& Attributes.node.getBooleanAttribute(id, "isExpansion." + networkName) ) {
				expansionNodeIds.add(id);
			}
            
        }
		
		for (MetabolicEdge edge : sourceNetwork.getEdges())
        {	
			CyEdge cyEdge = getTranslatorForEdge(edge).toCyEdge(edge);
			String id = cyEdge.getIdentifier();
            if(!network.containsEdge(cyEdge)) {
            	networkExpanded = true;
            	Attributes.edge.setAttribute(id, "isExpansion." + networkName, true);
            	network.addEdge(cyEdge);
            }
            if(!expandedFrom.contains(cyEdge.getSource().getIdentifier()) &&
            		!expandedFrom.contains(cyEdge.getTarget().getIdentifier()) 
            		&& Attributes.edge.getBooleanAttribute(id, "isExpansion." + networkName)) {
            	expansionEdgeIds.add(id);
            }
            
        }
		if(networkExpanded) {
			Attributes.node.setAttribute(cid, "isExpansionSeed." + networkName, true);
			for(String id: expansionNodeIds) {
				List<Object> inExpansionFor = new ArrayList<Object>();
				if(Attributes.node.hasAttribute(id,"inExpansionFor." + networkName)) 
					inExpansionFor.addAll
						(Attributes.node.getListAttribute(id, "inExpansionFor." + networkName));					
				if(!inExpansionFor.contains(cid)) {
					inExpansionFor.add(cid);
					Attributes.node.setListAttribute(id, "inExpansionFor." + networkName, inExpansionFor);
				}
			}
			for(String id: expansionEdgeIds) {
				List<Object> inExpansionFor = new ArrayList<Object>();
				if(Attributes.edge.hasAttribute(id,"inExpansionFor." + networkName))
					inExpansionFor.addAll
						(Attributes.edge.getListAttribute(id, "inExpansionFor." + networkName));					
				if(!inExpansionFor.contains(cid)) {
					inExpansionFor.add(cid);
					Attributes.edge.setListAttribute(id, "inExpansionFor." + networkName, inExpansionFor);
				}
			}
			String compoundDataName = compoundData.getName();
			String compoundPvalueColumn = Attributes.network.getStringAttribute(network.getIdentifier(), "CompoundPvalueColumn");
			Double compoundPvalueThreshold = Attributes.network.getDoubleAttribute(network.getIdentifier(), "CompoundPvalueThreshold");
			String compoundFoldChangeColumn = Attributes.network.getStringAttribute(network.getIdentifier(), "CompoundFoldChangeColumn");
			Double compoundFoldChangeThreshold = Attributes.network.getDoubleAttribute(network.getIdentifier(), "CompoundFoldChangeThreshold");
			
			String geneDataName = geneData.getName();
			String genePvalueColumn = Attributes.network.getStringAttribute(network.getIdentifier(), "GenePvalueColumn");
			Double genePvalueThreshold = Attributes.network.getDoubleAttribute(network.getIdentifier(), "GenePvalueThreshold");
			String geneFoldChangeColumn = Attributes.network.getStringAttribute(network.getIdentifier(), "GeneFoldChangeColumn");
			Double geneFoldChangeThreshold = Attributes.network.getDoubleAttribute(network.getIdentifier(), "GeneFoldChangeThreshold");
			
			Map<?,?> compoundPvalues =
				CyAttributesUtils.getAttribute("Compound." + compoundDataName + "." + compoundPvalueColumn, Cytoscape.getNodeAttributes());
			if(compoundPvalues != null)
			for(Object id: compoundPvalues.keySet()) {
				if(compoundPvalueThreshold == null || (Double) compoundPvalues.get(id) <= compoundPvalueThreshold) {
					Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,true);
				}
				else {
					Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,false);
				}
			}
			else {
				Map<?,?> hasData = CyAttributesUtils.getAttribute
					("Compound." + compoundDataName + ".hasData", Cytoscape.getNodeAttributes());
				if(hasData != null)
				for(Object id: hasData.keySet()) {
					if(hasData.get(id).equals(true))
						Attributes.node.setAttribute(id.toString(), "isSignificant."+networkName, true);
				}
			}
			
			Map<?,?> compoundFoldChanges =
				CyAttributesUtils.getAttribute("Compound." + compoundDataName + "." + compoundFoldChangeColumn, Cytoscape.getNodeAttributes());
			if(compoundFoldChanges != null) {
				Double upThreshold = 1.0;
				Double downThreshold = 1.0;
				if(compoundData.getColumnIsSigned().get(compoundFoldChangeColumn)) {
					upThreshold = Math.abs(compoundFoldChangeThreshold);
					downThreshold = -Math.abs(compoundFoldChangeThreshold);
				}
				else {
					if(compoundFoldChangeThreshold >= 1) {
						upThreshold = compoundFoldChangeThreshold;
						downThreshold = 1/compoundFoldChangeThreshold;
					}
					else if(compoundFoldChangeThreshold > 0) {
						upThreshold = 1/compoundFoldChangeThreshold;
						downThreshold = compoundFoldChangeThreshold;
					}
				}
				for(Object id: compoundFoldChanges.keySet()) {
					if((Double) compoundFoldChanges.get(id) >= upThreshold) {
						Attributes.node.setAttribute(id.toString(),"direction."+networkName,"up");
					}
					else if((Double) compoundFoldChanges.get(id) <= downThreshold) {
						Attributes.node.setAttribute(id.toString(),"direction."+networkName,"down");
					}
				}
			}
			
			Map<?,?> genePvalues =
				CyAttributesUtils.getAttribute("Gene." + geneDataName + "." + genePvalueColumn, Cytoscape.getNodeAttributes());
			if(genePvalues != null)
			for(Object id: genePvalues.keySet()) {
				if(genePvalueThreshold == null || (Double) genePvalues.get(id) <= genePvalueThreshold) {
					Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,true);
				}
				else {
					Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,false);
				}
			}
			else {
				Map<?,?> hasData = CyAttributesUtils.getAttribute
					("Gene." + geneDataName + ".hasData", Cytoscape.getNodeAttributes());
				if(hasData != null)
				for(Object id: hasData.keySet()) {
					if(hasData.get(id).equals(true))
						Attributes.node.setAttribute(id.toString(),"isSignificant."+networkName,true);
				}
			}
			
			Map<?,?> geneFoldChanges =
				CyAttributesUtils.getAttribute("Gene." + geneDataName + "." + geneFoldChangeColumn, Cytoscape.getNodeAttributes());
			if(geneFoldChanges != null) {
				Double upThreshold = 1.0;
				Double downThreshold = 1.0;
				if(geneData.getColumnIsSigned().get(geneFoldChangeColumn)) {
					upThreshold = Math.abs(geneFoldChangeThreshold);
					downThreshold = -Math.abs(geneFoldChangeThreshold);
				}
				else {
					if(geneFoldChangeThreshold >= 1) {
						upThreshold = geneFoldChangeThreshold;
						downThreshold = 1/geneFoldChangeThreshold;
					}
					else if(geneFoldChangeThreshold > 0) {
						upThreshold = 1/geneFoldChangeThreshold;
						downThreshold = geneFoldChangeThreshold;
					}
				}
				for(Object id: geneFoldChanges.keySet()) {
					if((Double) geneFoldChanges.get(id) >= upThreshold) {
						Attributes.node.setAttribute(id.toString(),"direction."+networkName,"up");
					}
					else if((Double) geneFoldChanges.get(id) <= downThreshold) {
						Attributes.node.setAttribute(id.toString(),"direction."+networkName,"down");
					}
				}
			}
			return network;
		}
		else return null;
	}

}
